/*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include <config.h>
#include <arch/kernel/xapic.h>
#include <arch/kernel/x2apic.h>

#ifdef CONFIG_XAPIC
#ifdef CONFIG_USE_LOGICAL_IDS
/* using flat cluster mode we only support 8 cores */
compile_assert(number_of_cores_invalid_for_logical_ids, CONFIG_MAX_NUM_NODES <= 8)
/*Z 设置本APIC逻辑ID为本cpu id，寻址为flat模式 */
BOOT_CODE static void
init_xapic_ldr(void)
{
    uint32_t ldr;
    /*Z 设置本APIC逻辑ID格式为平坦格式：LDR中高8位每位代表一个逻辑id */
    apic_write_reg(APIC_DEST_FORMAT, XAPIC_DFR_FLAT);
    ldr = apic_read_reg(APIC_LOGICAL_DEST) & MASK(XAPIC_LDR_SHIFT);/*Z 获取LDR保留位值 */
    ldr |= (BIT(getCurrentCPUIndex()) << XAPIC_LDR_SHIFT);/*Z 将本CPU id作为APIC逻辑ID */
    apic_write_reg(APIC_LOGICAL_DEST, ldr);/*Z 写入LDR寄存器 */
}
#endif /* CONFIG_USE_LOGICAL_IDS */
/*Z 使能APIC */
BOOT_CODE bool_t apic_enable(void)
{
    apic_base_msr_t apic_base_msr;  /*Z struct {u32[1]} */
    apic_base_msr.words[0] = x86_rdmsr_low(IA32_APIC_BASE_MSR);
    /*Z 检查APIC使能位 */
    if (!apic_base_msr_get_enabled(apic_base_msr)) {
        printf("APIC: Enabled bit not set\n");
        return false;
    }
    /*Z 检查处理器是否支持并使能x2APIC */
    if (x2apic_is_enabled()) {
        printf("x2APIC enabled in BIOS but kernel does not support that\n");
        return false;
    }
    /*Z 设置本APIC逻辑ID为本cpu id，寻址为平坦模式 */
#ifdef CONFIG_USE_LOGICAL_IDS
    init_xapic_ldr();
#endif /* CONFIG_USE_LOGICAL_IDS */

    return true;
}
/*Z 是否有中断pending */
bool_t apic_is_interrupt_pending(void)
{
    word_t i;

    /* read 256-bit register: each 32-bit word is 16 byte aligned */
    assert(int_irq_min % 32 == 0);
    for (i = int_irq_min; i <= int_irq_max; i += 32) {
        if (apic_read_reg(APIC_IRR_BASE + i / 2) != 0) {/*Z 获取APIC内存reg偏移处的值，u32 */
            return true;
        }
    }
    return false;
}
/*Z 向指定cpu发送初始化IPI */
BOOT_CODE void apic_send_init_ipi(cpu_id_t cpu_id)
{
    apic_write_icr(/*Z 向指定cpu发送初始化IPI */
        apic_icr2_new(
            cpu_id      /* dest *//*Z 目的cpu */
        ).words[0],
        apic_icr1_new(
            0,          /* dest_shorthand  *//*Z 广播等特殊目标模式。0-无 */
            1,          /* trigger_mode    *//*Z 边沿或水平触发。0: Edge   1: Level */
            1,          /* level           *//*Z 0-de-assert时为0，1-其它 */
            0,          /* delivery_status *//*Z 只读位：0-发送完毕，1-未完成 */
            0,          /* dest_mode       *//*Z 目标选择模式，0-物理，1-逻辑 */
            5,          /* delivery_mode   *//*Z 传送模式，5-INIT */
            0           /* vector          *//*Z INIT模式要求中断向量号为0 */
        ).words[0]
    );
    apic_write_icr(/*Z 向所有cpu发送arbitration ID设置IPI。写在这不好 */
        apic_icr2_new(
            cpu_id      /* dest */
        ).words[0],
        apic_icr1_new(
            0,          /* dest_shorthand  *//*Z 不好：按手册应为2 */
            1,          /* trigger_mode    */
            0,          /* level           */
            0,          /* delivery_status */
            0,          /* dest_mode       */
            5,          /* delivery_mode   *//*Z 5-INIT Level De-assert */
            0           /* vector          */
        ).words[0]
    );
}
/*Z 向指定cpu发送启动IPI，该cpu开始执行物理地址startup_addr处启动代码 */
BOOT_CODE void apic_send_startup_ipi(cpu_id_t cpu_id, paddr_t startup_addr)
{
    /* check if 4K aligned */
    assert(IS_ALIGNED(startup_addr, PAGE_BITS));
    /* check if startup_addr < 640K */
    assert(startup_addr < 0xa0000);/*Z 640K～1M为ROM */
    startup_addr >>= PAGE_BITS;/*Z 向量：(启动代码物理地址 >> 12) & 0xFF */

    apic_write_icr(
        apic_icr2_new(
            cpu_id       /* dest */
        ).words[0],
        apic_icr1_new(
            0,           /* dest_shorthand  */
            0,           /* trigger_mode    */
            0,           /* level           */
            0,           /* delivery_status */
            0,           /* dest_mode       */
            6,           /* delivery_mode   *//*Z 6-SIPI “start-up” IPI */
            startup_addr /* vector          *//*Z AP响应后置实模式CS:IP为0x0100:0x0000 */
        ).words[0]
    );
}
/*Z xapic的物理寻址模式下，向指定的单个cpu发送向量号为vector的IPI */
void apic_send_ipi_core(irq_t vector, cpu_id_t cpu_id)
{
    apic_icr1_t icr1;
    /* wait till we can send an IPI */
    do {
        icr1.words[0] = apic_read_reg(APIC_ICR1);
    } while (apic_icr1_get_delivery_status(icr1));

    apic_write_icr(
        apic_icr2_new(
            cpu_id      /* dest */
        ).words[0],
        apic_icr1_new(
            0,          /* dest_shorthand  */
            0,          /* trigger_mode    */
            0,          /* level           */
            0,          /* delivery_status */
            0,          /* dest_mode       *//*Z 0-物理寻址模式 */
            0,          /* delivery_mode   */
            vector      /* vector          */
        ).words[0]
    );
}
/*Z xapic的flat模式下，向mda代表的逻辑cpu id(s)发送向量号为vector的IPI */
void apic_send_ipi_cluster(irq_t vector, word_t mda)
{
    apic_icr1_t icr1;/*Z struct{u32[1]} */
    /* wait till we can send an IPI */
    do {
        icr1.words[0] = apic_read_reg(APIC_ICR1);/*Z 读中断命令寄存器(ICR)低32位值 */
    } while (apic_icr1_get_delivery_status(icr1));/*Z 直到先前的IPI发送完毕 */

    apic_write_icr(
        apic_icr2_new(
            mda         /* message destination address */
        ).words[0],
        apic_icr1_new(
            0,          /* dest_shorthand  */
            0,          /* trigger_mode    */
            0,          /* level           */
            0,          /* delivery_status */
            1,          /* dest_mode       *//*Z 1-逻辑寻址模式 */
            0,          /* delivery_mode   *//*Z 传送模式，0-固定向量 */
            vector      /* vector          */
        ).words[0]
    );
}
#endif /* CONFIG_XAPIC */
